package com.jiazhong.utils;

import org.apache.commons.beanutils.ConvertUtils;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;

public class Utils {
    /**
     * 赋值器方法，可以为一个实体对象的所有方法赋值
     * @param cla 类的字节码对象
     * @param paramsMap 对象中属性的值的一个存储器，其中key为属性名，value为属性值
     * @param <T> 类型参数，通用泛型
     *            该类型参数可以代表任意类型，在Class处传入的类型会自动赋给T
     * @return    已赋值的对象
     */
    public static <T> T setter(Class<T> cla, Map<String,Object> paramsMap) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchFieldException {
        /**
         * 创建类的对象：
         * 使用无参构造创建类的对象
         */
        T t = cla.getDeclaredConstructor().newInstance();
        /**遍历map集合，并为其中的相关属性赋值，实现思路：
         * 1.从Map集合中获取key（key是属性名），获得value（属性的值）
         * 2.根据属性名获取set方法的方法名
         * 3.根据属性名来获得属性对象
         * 4.根据方法名和属性类型来创建方法对象
         * 5.调用方法并用处理器赋值
         */
        for (Map.Entry<String ,Object> entry:paramsMap.entrySet()){
            //1.从Map集合中获取key（key是属性名），获得value（属性的值）
            String fieldName = entry.getKey();//获得属性名
            Object filedValue = entry.getValue();//获得属性值
            //2.根据属性名拼接方法名
            String methodName="set"+fieldName.substring(0,1).toUpperCase()+fieldName.substring(1);
            //3.根据属性名获取属性对象
            Field field = cla.getDeclaredField(fieldName);
            //4.根据方法名和属性类型获取方法对象
            Method method= cla.getDeclaredMethod(methodName,field.getType());
            //5.调用方法并用处理器赋值
            method.invoke(t,filedValue);
        }
        return t;
    }


    /**
     * 从request对象中获取请求的参数，并自动将这些参数封装为实体参数
     *    --约束条件：请求的参数的参数名必须为实体类中的属性名，并且大小写一致
     * @param cla  类的字节码对象
     * @param request  请求对象
     * @param <T>  类型参数，通用泛型
     *             该类型参数可以代表任意类型，在Class处传入的类型会自动赋给T
     * @return    已赋值的对象
     * @throws NoSuchMethodException
     * @throws InvocationTargetException
     * @throws InstantiationException
     * @throws IllegalAccessException
     * @throws NoSuchFieldException
     */
    public static <T> T encapsulationRequestToBean(Class<T> cla, HttpServletRequest request) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchFieldException, ParseException {
        //创建类的对象
        T t = cla.getDeclaredConstructor().newInstance();
        //获取request中的参数,返回一个map集合，map的key为属性名，value是属性值
        Map<String, String[]> parameterMap = request.getParameterMap();
        for (Map.Entry<String ,String[]> entry:parameterMap.entrySet()){
            //获取参数名
            String fieldName = entry.getKey();
            if (fieldName.equals("method")){
                continue;
            }
            //获得参数值中的第一个参数
            String fieldValue = entry.getValue()[0];
            //根据属性名拼接方法名
            String methodName="set"+fieldName.substring(0,1).toUpperCase()+fieldName.substring(1);
            //根据属性名获得属性对象
            Field field = cla.getDeclaredField(fieldName);
            //根据方法名和属性的类型来创建方法对象
            Method method= cla.getDeclaredMethod(methodName,field.getType());
            //调用方法
            //使用自定义类型转换
//            method.invoke(t,convertType(fieldValue,field.getType()));
            //使用BeanUtils依赖包进行类型转换
            method.invoke(t, ConvertUtils.convert(fieldValue,field.getType()));
        }
        return t;

    }

    /**
     * 自定义类型转换器
     * @param value   要转换的数据
     * @param type    要转换的类型
     * @return      转换好的数据
     */
    public static Object convertType(String value,Class type) throws ParseException {
        if (type==Integer.class){
            return Integer.parseInt(value);
        }
        if (type==Double.class){
            return Double.parseDouble(value);
        }
        if (type== Date.class){
            return new SimpleDateFormat("yyyy-MM-dd").parse(value);
        }
        return value;
    }
}
